# Thanks to https://github.com/ChadAMiller/hungergames for the template
# Author: Nachiket Apte

import random
class BasePlayer(object):
    '''
    Base class so I don't have to repeat bookkeeping stuff.
    '''
    def __str__(self):
        try:
            return self.name
        except AttributeError:
            # Fall back on Python default
            return super(BasePlayer, self).__repr__()
    
    def hunt_choices(*args, **kwargs):
        raise NotImplementedError("You must define a strategy!")
        
    def hunt_outcomes(*args, **kwargs):
        pass
        
    def round_end(*args, **kwargs):
        pass

class Player(BasePlayer):
    def __init__(self):
        self.name = "ReasonableHunter"
        self.firstRound = True
    
    # hunt with probability p, slack with probability (1 - p)
    def get(self, p):
        return 'h' if random.random() < p else 's'

    def hunt_choices(
                    self,
                    round_number,
                    current_food,
                    current_reputation,
                    m,
                    player_reputations,
                    ):
        
        # Total number of opponents (note: total number of players is num_players + 1)
        num_players = len(player_reputations)
        
        # The average / min / max reputations of all players still in the game
        avg_rep =  sum(player_reputations) / float(num_players)
        min_rep = min(player_reputations)
        max_rep = max(player_reputations)
        
        ''' 
        Fuzz all hardcoded probabilities based on the current round
        As the round number increases, the fuzz_factor increases and then stabilizes at 0.1
        ''' 
        fuzz_amount = min(0.1 * (float(round_number) / float(300)), .1)
        
        ''' 
        Is there a 50% or more chance of getting m this round?
        (We base the threshold on the average reputation so far)
        
        m_threshold < 1.0 : yes, with some wiggle room (no need to hunt with everyone)
        m_threshold == 1.0 : yes, no wiggle room (need to hunt with everyone)
        m_threshold > 1.0 : highly unlikely
        ''' 
        
        max_m_possible = avg_rep * num_players * (num_players - 1)
        if max_m_possible == 0:
            m_threshold = 1 # prevent ZeroDivisionErrors
        else:
            m_threshold = m / max_m_possible
        
        # Start building a decisions array
        hunt_decisions = []
        
        '''
        Strategies:
        
        CoinFlip: always flip a coin to make a decision.
        CounterBots: Always use dominant strategy against opponents with abnormally low reputations.
        EscapeHatch: if we're low on food, defer to CoinFlip.
        LateGame: Always use dominant strategy against a single opponent.
        Defensive: Hunt / slack against the max / min reputations respectively.
        Community: try to get the communal reward if it is within reach.
        AverageHunt: If our reputation is above average, hunt with opponents having a reputation above the average reputation. 
                     If our reputation is below average, hunt with opponents having a reputation above our reputation.
        '''
        
        for r in player_reputations:
            # CoinFlip Strategy
            if self.firstRound or abs(.5 - r) < fuzz_amount:
                ''' 
                Randomly hunt half of the times for the following cases
                 - it's the first round
                 - our opponent is perfectly random (reputation is ~0.5)
                '''
                decision = self.get(0.5)
            # CounterBots Strategy
            elif 1.0 - r < fuzz_amount or r < fuzz_amount:
                ''' 
                Counter player that always hunt or always slack with dominant strategy
                Use the fuzz_amount to catch clever bots with r == 0.99 or r == 0.01
                '''
                decision = self.get(0)
            # EscapeHatch strategy
            elif current_food <= 50 * num_players: # 300 starting food per opponent / 6 per hunt per opponent = 50
                ''' 
                We are running low on food. At this point anyone countering our standard strategy can be devastating to us, 
                so counter that by going random
                '''
                decision = self.get(.5)
            # LateGame strategy
            elif num_players == 1:
                '''
                Late game with just 1 opponent or less. Always slack with dominant strategy
                '''
                decision = self.get(0)
            # Defensive Strategy
            elif r == max_rep or r == min_rep:
                '''
                Slack against players with min_rep and hunt with players with max_rep
                '''
                decision = self.get(r)
            # Community Strategy
            elif m_threshold <= 1.0 - fuzz_amount:
                ''' 
                Looks like we might achieve m this round
                We hunt more actively this round. We slack against a small percentage of opponents based on fuzz_amount
                ''' 
                decision = self.get(m_threshold - fuzz_amount)
            # AverageHunt Strategy
            else:
                '''
                Fallback: this is our bread-and-butter strategy.
                We only hunt with opponents having a reputation above our reputation or the average reputation, whichever is lower
                '''
                decision = 'h' if r > min(avg_rep, current_reputation) else 's'
               
            hunt_decisions.append(decision)
        # End loop
        
        self.firstRound = False # the first round is always over at this point
        return hunt_decisions
    
    def hunt_outcomes(self, food_earnings):
        '''Required function defined in the rules'''
        pass
        

    def round_end(self, award, m, number_hunters):
        '''Required function defined in the rules'''
        pass
        